package pl.edu.agh.io.fx3.saver.dao;

import java.io.File;
import java.util.List;
import java.util.regex.Pattern;

import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;

import pl.edu.agh.io.fx3.exceptions.FileAlreadyExistsException;
import pl.edu.agh.io.fx3.exceptions.FileNotFoundException;
import pl.edu.agh.io.fx3.saver.MyBatisConnectionFactory;
import pl.edu.agh.io.fx3.saver.model.FileInfo;

/**
 * Represents basic methods operating on files and directories.
 * @author Konrad Tendera
 *
 */
public class FileUtilsDAO {
	
	private SqlSessionFactory sqlSessionFactory; 
	
	public FileUtilsDAO() {
		sqlSessionFactory = MyBatisConnectionFactory.getSqlSessionFactory();
	}
	
	/**
	 * Returns the list of all {@link FileInfo} instances from the database.
	 * @param dirPath Path to virtual directory.
	 * @return List of files in directory given as argument.
	 * @throws FileNotFoundException If path not found.
	 */
	public List<FileInfo> listDir(String dirPath) throws FileNotFoundException {
		SqlSession session = sqlSessionFactory.openSession();

		if (dirPath.endsWith(File.separator + File.separator)) {
			dirPath += "%";
		} else if (dirPath.endsWith(File.separator)) {
			dirPath += File.separator + "%";
		} else {
			if (File.separator.equals("\\")) {
				String replaceString = File.separator + File.separator + File.separator + File.separator;
				dirPath = dirPath.replaceAll(Pattern.quote(File.separator), replaceString);
				dirPath += File.separator + File.separator + "%";
			} else {
				dirPath += File.separator + "%";
			}
		}
		try {
			List<FileInfo> list = session.selectList("FileInfo.list", dirPath);
			return list;
		} catch (Exception e) {
			throw new FileNotFoundException(dirPath);
		} finally {
			session.close();
		}
	}
	
	/**
	 * Inserts an instance of {@link FileInfo} into the database.
	 * @param virtualPath Virtual path of inserted file.
	 * @param realPath Real path of inserted file.
	 * @param checksum Checksum of inserted file.
	 * @throws FileAlreadyExistsException If virtualPath already exists.
	 */
	public void insert(String virtualPath, String realPath, String checksum) throws FileAlreadyExistsException {
		SqlSession session = sqlSessionFactory.openSession();

		try {
			session.insert("FileInfo.insert", new FileInfo(virtualPath, realPath, checksum));
			session.commit();
		} catch (Exception e) {
			throw new FileAlreadyExistsException(virtualPath);
		} finally {
			session.close();
		}
	}
	
	/**
	 * Changes given real path to another.
	 * @param realPath Real path to change.
	 * @param newPath New value of real path.
	 * @throws FileNotFoundException If path to change does not exist.
	 */
	public void changeRealPath(String realPath, String newPath) throws FileNotFoundException {
		SqlSession session = sqlSessionFactory.openSession();

		try {
			session.update("FileInfo.changeReal", new FileInfo(realPath, newPath, null));
			session.commit();
		} catch (Exception e) {
			throw new FileNotFoundException(realPath);
		} finally {
			session.close();
		}
	}
	
	/**
	 * Changes given virtual path to new one.
	 * @param virtualPath Virtual path to change.
	 * @param newPath New value of virtual path.
	 * @throws FileNotFoundException If path to change does not exist.
	 */
	public void changeVirtualPath(String virtualPath, String newPath) throws FileNotFoundException {
		SqlSession session = sqlSessionFactory.openSession();

		try {
			session.update("FileInfo.changeVirtual", new FileInfo(virtualPath, newPath, null));
			session.commit();
		} catch (Exception e) {
			throw new FileNotFoundException(virtualPath);
		} finally {
			session.close();
		}
	}
	
	/**
	 * Deletes row using virtual path.
	 * @param virtualPath Virtual path which determines file to delete.
	 * @throws FileNotFoundException If path to delete does not exist.
	 */
	public void deleteUsingVirtualPath(String virtualPath) throws FileNotFoundException {
		SqlSession session = sqlSessionFactory.openSession();

		try {
			session.delete("FileInfo.deleteVirtual", virtualPath);
			session.commit();
		} catch (Exception e) {
			throw new FileNotFoundException(virtualPath);
		} finally {
			session.close();
		}
	}
	
	/**
	 * Deletes row using real path.
	 * @param realPath Real path which determines file to delete.
	 * @throws FileNotFoundException If path to delete does not exist. 
	 */
	public void deleteUsingRealpath(String realPath) throws FileNotFoundException {
		SqlSession session = sqlSessionFactory.openSession();

		try {
			session.delete("FileInfo.deleteReal", realPath);
			session.commit();
		} catch (Exception e) {
			throw new FileNotFoundException(realPath);
		} finally {
			session.close();
		}
	}
	
	/**
	 * Gets parent directory of given file.
	 * @param filePath File to check parent directory.
	 * @return Parent directory name of file given as parameter.
	 * @throws FileNotFoundException If file given as parameter does not exist.
	 */
	public String getParentDir(String filePath) throws FileNotFoundException {
		SqlSession session = sqlSessionFactory.openSession();

		try {
			String result = session.selectOne("FileInfo.getParentDir", filePath);
			if (result == null) {
				throw new FileNotFoundException(filePath);
			} else {
				return result.substring(0, result.lastIndexOf(File.separator));
			}
		} finally {
			session.close();
		}
	}

	/**
	 * Gets real path of given virtual path.
	 * @param virtualPath Virtual path of file to check.
	 * @return Real path of file given as parameter.
	 * @throws FileNotFoundException If file given as parameter does not exist.
	 */
	public String getRealPath(String virtualPath) throws FileNotFoundException {
		SqlSession session = sqlSessionFactory.openSession();
		
		try {
			String result = session.selectOne("FileInfo.getRealPath", virtualPath);
			if (result == null) {
				throw new FileNotFoundException(virtualPath);
			} else {
				return result;
			}
		} finally {
			session.close();
		}
	}
	
	/**
	 * Gets virtual path of given real path.
	 * @param realPath Real path of file to check.
	 * @return Virtual path of file given as parameter.
	 * @throws FileNotFoundException If file given as parameter does not exist.
	 */
	public String getVirtualPath(String realPath) throws FileNotFoundException {
		SqlSession session = sqlSessionFactory.openSession();
		
		try {
			String result = session.selectOne("FileInfo.getVirtualPath", realPath);
			if (result == null) {
				throw new FileNotFoundException(realPath);
			} else {
				return result;
			}
		} finally {
			session.close();
		}
	}
	
	/**
	 * Checks if file given as parameter exists.
	 * @param path File path to change if exists.
	 * @return True if file exists, false otherwise.
	 */
	public boolean exists(String path) {
		SqlSession session = sqlSessionFactory.openSession();
		
		try {
			List<FileInfo> list = session.selectList("FileInfo.exists", path);
			return !list.isEmpty();
		} finally {
			session.close();
		}
	}
}
